package com.hawkfly.charts;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;

import com.wbcs.config.Config;
import com.wbcs.config.component.application.report.ColBean;
import com.wbcs.config.component.application.report.DisplayBean;
import com.wbcs.config.component.application.report.ReportBean;
import com.wbcs.config.xml.XmlAssistant;
import com.wbcs.system.ReportRequest;
import com.wbcs.system.assistant.ReportDataAssistant;
import com.wbcs.system.component.application.report.UltraListReportType;
import com.wbcs.system.intercept.AbsInterceptorDefaultAdapter;

/**
 * Charts数据处理拦截处理器
 * @author hawkfly
 */
public class InterceptorFusionChartsXmlDatasConvertor extends AbsInterceptorDefaultAdapter
{
    @Override
	public Object afterLoadData(ReportRequest rrequest,ReportBean rbean,Object typeObj,Object dataObj)
    {
        Object obj = ReportDataAssistant.getInstance().getColValue(rrequest,rbean.getId(),"rolecode",((List)dataObj).get(0));
        ReportDataAssistant.getInstance().setColValue(rrequest,rbean.getId(),"rolecode",((List)dataObj).get(0),"test");
        if(typeObj instanceof UltraListReportType){
        //获取以page+report名字命名指定目录下的XML文件，并将其加载到内存中
            initXMLDocument(rbean);
            if(document == null)rrequest.getWResponse().getMessageCollector().error("读取本Chart的XML配置文件失败，请检查你的配置！",true);
            Element rootElement = document.getRootElement();
            List al = (ArrayList)dataObj;
            if(al.size() == 0){reponseOnloadChartsAjax(rrequest, rbean, getDefaultChartXml(rbean), 0);return dataObj;}
        //执行报表分析
            List<String> chartname = initChart(rrequest, rbean);
            for(int i=0;i<chartname.size();i++)
            {
                analyseChart(rrequest, rbean, al, rootElement, chartname.get(i));
                //写回客户端
                reponseOnloadChartsAjax(rrequest, rbean, document.asXML(), i);
            }
        }
        return dataObj;
    }
    
    List<String> initChart(ReportRequest rrequest, ReportBean rbean){//返回chartname
      //得到display的colselectwidth="700" colselectmaxheight="400" dataheader="../sss/chart,renderdiv" 三个值并将其重置为空
        DisplayBean dbean = rbean.getDbean();
        width = Integer.parseInt("100%".equals(rbean.getWidth())?"0":rbean.getWidth()); 
        height = Integer.parseInt("100%".equals(rbean.getHeight())?"0":rbean.getHeight());
        String chartpathstr = rbean.getExtXmlBean().attributeValue("chartpath");
        String renderAtstr  = rbean.getExtXmlBean().attributeValue("renderAts");
        if(chartpathstr == null || renderAtstr == null)rrequest.getWResponse().getMessageCollector().error("配置的chart路径或渲染层为空，请检查你的display标签的dataheader配置",true);
        String[] chartpaths = chartpathstr.split(",");
        chartpath = new ArrayList<String>();
        List<String> chartname = new ArrayList<String>();
        for(int i = 0; i < chartpaths.length; i++){
            chartpath.add(new StringBuffer(Config.webroot).append(chartpaths[i]).toString());
            chartname.add(chartpaths[i].substring(chartpaths[i].lastIndexOf("/")+1, chartpaths[i].lastIndexOf(".")));
        }
        String[] renderAts = renderAtstr.split(",");
        renderAt = new ArrayList<String>();
        for(int i=0;i<renderAts.length;i++)
        {
            renderAt.add(renderAts[i]);
        }
        
        //统一chartname和renderAt的长度
        if(renderAt.size() < chartname.size())rrequest.getWResponse().getMessageCollector().error("renderAt的长度不能小于图表的个数",true);
        if(renderAt.size() > chartname.size()){//renderAt多于chart，则默认以chart的第一个进行填充
            for(int i = 0; i < renderAt.size() - chartname.size(); i++){
                chartname.add(chartname.get(0));
            }
        }
        return chartname;
    }
    
    /**
     * 分析当前chart是什么图形，以便执行不同的处理
     * @param root chartElement
     */
    void analyseChart(ReportRequest rrequest, ReportBean rbean, List al, Element rootElement, String chartname){
        
        switch(Chartypes.valueOf(chartname)){
            case AngularGauge://仪表图
                doAngularGaugeChart(rrequest, rbean, rootElement, al);
                break;
            case HBullet://bullet图
                doHbulletChart(rrequest, rbean, rootElement, al);
                break;
            case MSLine://默认的柱状图及曲线图
            case MSColumn2D:
            case MSColumn3D:
                doDefaultChart(rrequest, rbean, rootElement, al, "MSLine");
                break;
            case Column2D:
            case Column3D:
                doDefaultChart(rrequest, rbean, rootElement, al, "MSColumn");
                break;
        }
    }
    
    void doDefaultChart(ReportRequest rrequest, ReportBean rbean, Element rootElement, List al, String defaultchart){
        //将dataObj执行结果转换为符合fusionChartXml规范的XMLStr（在定义wbcsXML文件的COL的property属性时如果是Label列则为***Label,若是Value列则为***Value）
        //获取dataObj集合中的第一个对象的所有方法名 Method methlist[] = cls.getDeclaredMethods();
        List<Object> lstLabels = new ArrayList<Object>();
        Map<String, List<Object>> mvalues = new HashMap<String, List<Object>>();//key由label和ColBean编号组成，例如：labelname-1
        lstColBean = rbean.getDbean().getLstCols();
        for(int i=0;i<al.size();i++){
            for(int j=0;j<lstColBean.size();j++){
                String p = lstColBean.get(j).getProperty();
                if(p.endsWith("LABEL")){
                    if(lstLabels.size() == 0)lstLabels.add(j);//label集合的0下标位置永远是ColBean的编号，后面可以通过lstColBean.get(编号)的方式取出此列的配置信息
                    lstLabels.add(ReportDataAssistant.getInstance().getColValue(rrequest,rbean.getId(),p,al.get(i)));  
                }
                if(p.endsWith("VALUE")) {
                    String label = lstColBean.get(j).getLabel();
                    if(label == null)label = "";
                    String keyname = new StringBuffer().append(label).append("-").append(j).toString();//key的格式是labelName-colnum
                    if(mvalues.containsKey(keyname))
                        mvalues.get(keyname).add(ReportDataAssistant.getInstance().getColValue(rrequest,rbean.getId(),p,al.get(i)));
                    else{
                        List<Object> linevalues = new ArrayList<Object>();
                        linevalues.add(ReportDataAssistant.getInstance().getColValue(rrequest,rbean.getId(),p,al.get(i)));
                        mvalues.put(keyname,linevalues);
                    }  
                }
            }
        }
       //分析label和value并将其区分开来创建对应的Element对象(label+value=2和label+value>2情况不同)
       //将所有的Element对象插入到第二步XML缓冲串的指定位置
        if("MSColumn".equals(defaultchart)){//<set label="a" value="35"/>
            doMSColumnChart(rootElement, lstLabels, mvalues);
        }else if("MSLine".equals(defaultchart)){//<set value="35"/>
            doMSLineChart(rootElement, lstLabels, mvalues);
        }
    }
    
    void doAngularGaugeChart(ReportRequest rrequest, ReportBean rbean, Element rootElement, List al){
        //将al转换成List<Map<String, Object>>结构
        List<Map<String, Object>> lstdata = new ArrayList<Map<String, Object>>();
        IChartData ichartdata = doPreChartHasI(rrequest, rbean, al, lstdata);
        //转换为IChartData接口并执行其方法
        Map<String, Map<String, Map<String, Object>>> map = ichartdata.doGauge(lstdata);
        //将返回值Map<String, List<Map<String, Object>>>转换为Element并追加给rootElement
        for(String key1 : map.keySet()){//<dials></dials>||<trendpoints></trendpoints>
            Element element = rootElement.addElement(key1);
            Map<String, Map<String, Object>> msubelements = map.get(key1);//<dial>||<point>
            for(String key2 : msubelements.keySet()){
                Element subelement = element.addElement(key2);
                Map<String, Object> subelementAttrs = msubelements.get(key2);
                for(String key3 : subelementAttrs.keySet()){
                    subelement.addAttribute(key3,String.valueOf(subelementAttrs.get(key3)));
                }
            }
        }
    }
    
    void doHbulletChart(ReportRequest rrequest, ReportBean rbean, Element rootElement, List al){
        List<Map<String, Object>> lstdata = new ArrayList<Map<String, Object>>();
        IChartData ichartdata = doPreChartHasI(rrequest, rbean, al, lstdata);
        List<Map<String, Object>> list = ichartdata.doBullet(lstdata);
        for(Map<String, Object> item : list){
            for(String key : item.keySet()){
              //upperLimit
                if("upperLimit".equals(key))rootElement.addAttribute(key,String.valueOf(item.get(key)));
              //value、target
                if("value".equals(key) || "target".equals(key))rootElement.addElement(key).addText(String.valueOf(item.get(key)));
            }
        }
    }
    
    void doMSLineChart(Element rootElement, List<Object> lstLabels, Map<String, List<Object>> mvalues){
        parseListCategoriesToElement(rootElement, lstLabels);
        parseListDataSetToElement(rootElement, mvalues);
    }
    
    void doMSColumnChart(Element rootElement, List<Object> lstLabels, Map<String, List<Object>> mvalues){
        List<Object> lstValueHasColBeanNum = new ArrayList<Object>();
        String firstkey = mvalues.keySet().iterator().next();
        lstValueHasColBeanNum.add(parseKeyToColBeanNum(firstkey));
        lstValueHasColBeanNum.addAll(mvalues.get(firstkey));
        parseListDataSetToElement(rootElement, lstLabels, lstValueHasColBeanNum);
    }
    
    private IChartData doPreChartHasI(ReportRequest rrequest, ReportBean rbean, List srd, List<Map<String, Object>> desd){
        lstColBean = rbean.getDbean().getLstCols();
        for(int i=0;i<srd.size();i++){//one row
            Map<String, Object> mrow = new HashMap<String, Object>(); desd.add(mrow);
            for(int j=0;j<lstColBean.size();j++){
               String p = lstColBean.get(j).getProperty();
               mrow.put(p, ReportDataAssistant.getInstance().getColValue(rrequest,rbean.getId(),p,srd.get(i)));
            }
        }
        //取得report中的class属性的值并清空
        String ichartdataimpl = rbean.getExtXmlBean().attributeValue("ichartdataimpl");
        IChartData ichartdata = null;
        try
        {
            ichartdata = (IChartData)Class.forName(ichartdataimpl).newInstance();
        }catch(InstantiationException e)
        {
            rrequest.getWResponse().getMessageCollector().error("report的ichartdataimpl参数初始化错误",true);
            e.printStackTrace();
        }catch(IllegalAccessException e)
        {
            rrequest.getWResponse().getMessageCollector().error("report的ichartdataimpl参数传递错误",true);
            e.printStackTrace();
        }catch(ClassNotFoundException e)
        {
            rrequest.getWResponse().getMessageCollector().error("report的ichartdataimpl路径配置有误",true);
            e.printStackTrace();
        }
        return ichartdata;
    }
    
    public String parseXMLName(String pageid, String reportid, StringBuffer path){
        if(pageid == null || reportid == null)return path.toString();
        String first = reportid.substring(0,1).toUpperCase();
        String rest = reportid.substring(1, reportid.length());
        //pagereportid = new StringBuffer(pageid).append(first).append(rest).toString();
        return  path.append(pageid).append(first).append(rest).append(".xml").toString();
    }
    
    Element parseListCategoriesToElement(Element parent, List<Object> lstCategory){
        Element categoriesElement = parent.addElement("categories");
        Object colBeanNum = lstCategory.get(0);
        if(colBeanNum == null){
            log.error("label的COLBEAN不存在！");
            return parent;
        }
        ColBean colBean = lstColBean.get((Integer)colBeanNum);
        for(int i = 1; i < lstCategory.size(); i++)
        {
            categoriesElement.addElement("category").addAttribute("label", colBean.getDatatypeObj().value2label(lstCategory.get(i)));
        }
        return parent;
    }
    
    Element parseListDataSetToElement(Element parent, Map<String, List<Object>> mvalues){
        for(String key:mvalues.keySet())
        {
            ColBean colBean = lstColBean.get(parseKeyToColBeanNum(key));
            String labelStylePropertyStr = colBean.getLabelstyleproperty();
            Element categoriesElement = parent.addElement("dataset").addAttribute("seriesName",excludeKey1Unit(key))
                                        .addAttribute("color",stylePropertyToMap(labelStylePropertyStr).get("color"));
            for(Object item:mvalues.get(key))
            {
                categoriesElement.addElement("set").addAttribute("value", colBean.getDatatypeObj().value2label(item));
            }
        }
        return parent;
    }
    
    Element parseListDataSetToElement(Element parent, List<Object> lstLabels, List<Object> lstValues){
        Integer colBeanNumAsLabel = (Integer)lstLabels.get(0);
        Integer colBeanNumAsValue = (Integer)lstValues.get(0);
        for(int i=1;i<lstLabels.size();i++)
        {
            parent.addElement("set")
            .addAttribute("label",lstColBean.get(colBeanNumAsLabel).getDatatypeObj().value2label(lstLabels.get(i)))
            .addAttribute("value",lstColBean.get(colBeanNumAsValue).getDatatypeObj().value2label(lstValues.get(i)));
        }
        return parent;
    }
    
    int parseKeyToColBeanNum(String key){
        String[] keyAry = key.split("-");
        if(keyAry.length != 2){
            log.error("key配置有问题，格式应该是label-num");
            return -1;
        }
        return Integer.parseInt(keyAry[1]);
    }
    
    String excludeKey1Unit(String key){
        String[] keyAry = key.split("-");
        if(keyAry.length != 2){
            log.error("key配置有问题，格式应该是label-num");
            return "";
        }
        return keyAry[0];
    }
    
    Map<String, String> stylePropertyToMap(String styleProperty){
        Map<String, String> map = new HashMap<String, String>();
        String[] styleProperties = styleProperty.split(" ");
        for(String item:styleProperties)
        {
            String[] items = item.trim().split("=");
            if(items.length != 2)log.error(new StringBuffer().append(item).append(":此labelstyleproperty配置的格式不正确！"));
            map.put(items[0].trim(),items[1].trim().replace("'",""));
        }
        return map;
    }
    
    void reponseOnloadChartsAjax(ReportRequest rrequest, ReportBean rbean, String xmlStr, int num){
        rbean.getPageBean().addMyJavascriptFile("/webresources/jsplugins/FusionCharts.js",-1);
        xmlStr = parseXMLStrExcludeTNR(xmlStr);
        StringBuffer paramsBuf=new StringBuffer();
        paramsBuf.append("{swfUrl:'").append(chartpath.get(num)).append("' ,renderAt:'").append(renderAt.get(num)).append("', width:").append(width)
        .append(", height:").append(height).append(", dataSource:'").append(xmlStr).append("', dataFormat:'").append("xml' }"); 
        //rrequest.getWResponse().addOnloadMethod(new StringBuffer("onload").append(pagereportid == null ? "" : pagereportid).toString(),paramsBuf.toString(),false);
        rrequest.getWResponse().addOnloadMethod("FusionCharts.render",paramsBuf.toString(),false);
    }
    
    String getDefaultChartXml(ReportBean rbean){
        if(pureDocument == null)initXMLDocument(rbean);
        return parseXMLStrExcludeTNR(pureDocument.asXML());
    }
    
    void initXMLDocument(ReportBean rbean){
        StringBuffer chartxmlppath = new StringBuffer().append("../../").append(Config.getInstance().getSystemConfigValue("chart-xml-home","")).append("/");
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(parseXMLName(rbean.getPageBean().getId(), rbean.getId(), chartxmlppath));
        try
        {
            pureDocument = XmlAssistant.getInstance().loadXmlDocument(in);
            document = (Document)pureDocument.clone();
        }catch(DocumentException e)
        {
            e.printStackTrace();
        }finally{
            try
            {
                in.close();
            }catch(IOException e)
            {
                e.printStackTrace();
            }
        }
    }
    
    String parseXMLStrExcludeTNR(String xmlStr){
        Pattern p = Pattern.compile("\t|\r|\n");
        Matcher m = p.matcher(xmlStr);
        xmlStr = m.replaceAll("");
        return xmlStr;
    }
    
    String doc2String(Document document){ 
        String s = ""; 
        try 
        { 
             //使用输出流来进行转化 
             ByteArrayOutputStream out = new ByteArrayOutputStream(); 
             //使用UTF-8编码 
             OutputFormat format = OutputFormat.createPrettyPrint(); 
             XMLWriter writer = new XMLWriter(out, null); 
             writer.write(document); 
             s = out.toString("UTF-8"); 
        }catch(Exception ex) 
        {             
             ex.printStackTrace(); 
        }       
        return s; 
     }
    
    public enum Chartypes{
        Area2D,Bar2D,MSLine,Bubble,Column2D,Column3D,Doughnut2D,Doughnut3D,FCExporter,AngularGauge,
        Line,Marimekko,MSArea,MSBar2D,MSBar3D,MSColumn2D,MSColumn3D,MSColumn3DLineDY,MSColumnLine3D,
        MSCombi2D,MSCombi3D,MSCombiDY2D,MSStackedColumn2D,MSStackedColumn2DLineDY,Pareto2D,Pareto3D,
        Pie2D,Pie3D,Scatter,ScrollColumn2D,ScrollCombi2D,ScrollCombiDY2D,ScrollLine2D,ScrollStackedColumn2D,
        SSGrid,StackedArea2D,StackedBar2D,StackedBar3D,StackedColumn2D,StackedColumn2DLine,StackedColumn3D,
        StackedColumn3DLine,StackedColumn3DLineDY,ZoomLine,HBullet;
     }
    
    private static Log log = LogFactory.getLog(InterceptorFusionChartsXmlDatasConvertor.class);
    private List<ColBean> lstColBean;
    private Document document;
    private Document pureDocument;
    //private String pagereportid;
    private List<String> chartpath;
    private List<String> renderAt;
    private int width;
    private int height;
}